{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n# Path Tutorial\n\n\nDefining paths in your Matplotlib visualization.\n\nThe object underlying all of the :mod:`matplotlib.patch` objects is\nthe :class:`~matplotlib.path.Path`, which supports the standard set of\nmoveto, lineto, curveto commands to draw simple and compound outlines\nconsisting of line segments and splines.  The ``Path`` is instantiated\nwith a (N,2) array of (x,y) vertices, and a N-length array of path\ncodes.  For example to draw the unit rectangle from (0,0) to (1,1), we\ncould use this code\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\nfrom matplotlib.path import Path\nimport matplotlib.patches as patches\n\nverts = [\n   (0., 0.),  # left, bottom\n   (0., 1.),  # left, top\n   (1., 1.),  # right, top\n   (1., 0.),  # right, bottom\n   (0., 0.),  # ignored\n]\n\ncodes = [\n    Path.MOVETO,\n    Path.LINETO,\n    Path.LINETO,\n    Path.LINETO,\n    Path.CLOSEPOLY,\n]\n\npath = Path(verts, codes)\n\nfig, ax = plt.subplots()\npatch = patches.PathPatch(path, facecolor='orange', lw=2)\nax.add_patch(patch)\nax.set_xlim(-2, 2)\nax.set_ylim(-2, 2)\nplt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The following path codes are recognized\n\n============== =================================  ====================================================================================================================\nCode           Vertices                           Description\n============== =================================  ====================================================================================================================\n``STOP``       1 (ignored)                        A marker for the end of the entire path (currently not required and ignored)\n``MOVETO``     1                                  Pick up the pen and move to the given vertex.\n``LINETO``     1                                  Draw a line from the current position to the given vertex.\n``CURVE3``     2 (1 control point, 1 endpoint)    Draw a quadratic Bézier curve from the current position, with the given control point, to the given end point.\n``CURVE4``     3 (2 control points, 1 endpoint)   Draw a cubic Bézier curve from the current position, with the given control points, to the given end point.\n``CLOSEPOLY``  1 (point itself is ignored)        Draw a line segment to the start point of the current polyline.\n============== =================================  ====================================================================================================================\n\n\n.. path-curves:\n\n\nBézier example\n==============\n\nSome of the path components require multiple vertices to specify them:\nfor example CURVE 3 is a `bézier\n<https://en.wikipedia.org/wiki/B%C3%A9zier_curve>`_ curve with one\ncontrol point and one end point, and CURVE4 has three vertices for the\ntwo control points and the end point.  The example below shows a\nCURVE4 Bézier spline -- the bézier curve will be contained in the\nconvex hull of the start point, the two control points, and the end\npoint\n\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "verts = [\n   (0., 0.),   # P0\n   (0.2, 1.),  # P1\n   (1., 0.8),  # P2\n   (0.8, 0.),  # P3\n]\n\ncodes = [\n    Path.MOVETO,\n    Path.CURVE4,\n    Path.CURVE4,\n    Path.CURVE4,\n]\n\npath = Path(verts, codes)\n\nfig, ax = plt.subplots()\npatch = patches.PathPatch(path, facecolor='none', lw=2)\nax.add_patch(patch)\n\nxs, ys = zip(*verts)\nax.plot(xs, ys, 'x--', lw=2, color='black', ms=10)\n\nax.text(-0.05, -0.05, 'P0')\nax.text(0.15, 1.05, 'P1')\nax.text(1.05, 0.85, 'P2')\nax.text(0.85, -0.05, 'P3')\n\nax.set_xlim(-0.1, 1.1)\nax.set_ylim(-0.1, 1.1)\nplt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    ".. compound_paths:\n\nCompound paths\n==============\n\nAll of the simple patch primitives in matplotlib, Rectangle, Circle,\nPolygon, etc, are implemented with simple path.  Plotting functions\nlike :meth:`~matplotlib.axes.Axes.hist` and\n:meth:`~matplotlib.axes.Axes.bar`, which create a number of\nprimitives, e.g., a bunch of Rectangles, can usually be implemented more\nefficiently using a compound path.  The reason ``bar`` creates a list\nof rectangles and not a compound path is largely historical: the\n:class:`~matplotlib.path.Path` code is comparatively new and ``bar``\npredates it.  While we could change it now, it would break old code,\nso here we will cover how to create compound paths, replacing the\nfunctionality in bar, in case you need to do so in your own code for\nefficiency reasons, e.g., you are creating an animated bar plot.\n\nWe will make the histogram chart by creating a series of rectangles\nfor each histogram bar: the rectangle width is the bin width and the\nrectangle height is the number of datapoints in that bin.  First we'll\ncreate some random normally distributed data and compute the\nhistogram.  Because numpy returns the bin edges and not centers, the\nlength of ``bins`` is 1 greater than the length of ``n`` in the\nexample below::\n\n    # histogram our data with numpy\n    data = np.random.randn(1000)\n    n, bins = np.histogram(data, 100)\n\nWe'll now extract the corners of the rectangles.  Each of the\n``left``, ``bottom``, etc, arrays below is ``len(n)``, where ``n`` is\nthe array of counts for each histogram bar::\n\n    # get the corners of the rectangles for the histogram\n    left = np.array(bins[:-1])\n    right = np.array(bins[1:])\n    bottom = np.zeros(len(left))\n    top = bottom + n\n\nNow we have to construct our compound path, which will consist of a\nseries of ``MOVETO``, ``LINETO`` and ``CLOSEPOLY`` for each rectangle.\nFor each rectangle, we need 5 vertices: 1 for the ``MOVETO``, 3 for\nthe ``LINETO``, and 1 for the ``CLOSEPOLY``.  As indicated in the\ntable above, the vertex for the closepoly is ignored but we still need\nit to keep the codes aligned with the vertices::\n\n    nverts = nrects*(1+3+1)\n    verts = np.zeros((nverts, 2))\n    codes = np.ones(nverts, int) * path.Path.LINETO\n    codes[0::5] = path.Path.MOVETO\n    codes[4::5] = path.Path.CLOSEPOLY\n    verts[0::5,0] = left\n    verts[0::5,1] = bottom\n    verts[1::5,0] = left\n    verts[1::5,1] = top\n    verts[2::5,0] = right\n    verts[2::5,1] = top\n    verts[3::5,0] = right\n    verts[3::5,1] = bottom\n\nAll that remains is to create the path, attach it to a\n:class:`~matplotlib.patch.PathPatch`, and add it to our axes::\n\n    barpath = path.Path(verts, codes)\n    patch = patches.PathPatch(barpath, facecolor='green',\n      edgecolor='yellow', alpha=0.5)\n    ax.add_patch(patch)\n\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "import numpy as np\nimport matplotlib.patches as patches\nimport matplotlib.path as path\n\nfig, ax = plt.subplots()\n# Fixing random state for reproducibility\nnp.random.seed(19680801)\n\n# histogram our data with numpy\ndata = np.random.randn(1000)\nn, bins = np.histogram(data, 100)\n\n# get the corners of the rectangles for the histogram\nleft = np.array(bins[:-1])\nright = np.array(bins[1:])\nbottom = np.zeros(len(left))\ntop = bottom + n\nnrects = len(left)\n\nnverts = nrects*(1+3+1)\nverts = np.zeros((nverts, 2))\ncodes = np.ones(nverts, int) * path.Path.LINETO\ncodes[0::5] = path.Path.MOVETO\ncodes[4::5] = path.Path.CLOSEPOLY\nverts[0::5, 0] = left\nverts[0::5, 1] = bottom\nverts[1::5, 0] = left\nverts[1::5, 1] = top\nverts[2::5, 0] = right\nverts[2::5, 1] = top\nverts[3::5, 0] = right\nverts[3::5, 1] = bottom\n\nbarpath = path.Path(verts, codes)\npatch = patches.PathPatch(barpath, facecolor='green',\n                          edgecolor='yellow', alpha=0.5)\nax.add_patch(patch)\n\nax.set_xlim(left[0], right[-1])\nax.set_ylim(bottom.min(), top.max())\n\nplt.show()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
